#include "dkglBox.h"
#include "dkglPreRequest.h"
#include "dkglOBB.h"

namespace DKGL
{
    namespace primitive
    {
        Box::Box(const String& name, float width, float height, float depth) :
            Mesh(name),
            displayListId(0),
			_obb(NULL),
			_size(width, height, depth)
        {
            allocBuf();
            initVertexBuf(width, height, depth);
            initNormalBuf();
            initTexcoordBuf();
            genDisplayList();
        }

        Box::~Box()
        {
            if(displayListId)
                glDeleteLists(displayListId, 1);

			delete _obb;
        }


		void Box::setMaterial(int, MaterialPtr matPtr)
		{
			_materialPtr = matPtr;
		}

        void Box::allocBuf()
        {
            // allocate 8 vertices
            vertexBuf.alloc(24);
            normalBuf.alloc(24);
            texcoordBuf.alloc(24);
        }

        void Box::initVertexBuf(float width, float height, float depth)
        {
            float x = width/2;
            float y = height/2;
            float z = depth/2;

            // +X
            vertexBuf.set(0, x, -y, z);
            vertexBuf.set(1, x, -y, -z);
            vertexBuf.set(2, x, y, -z);
            vertexBuf.set(3, x, y, z);

            // -X
            vertexBuf.set(4, -x, -y, -z);
            vertexBuf.set(5, -x, -y, z);
            vertexBuf.set(6, -x, y, z);
            vertexBuf.set(7, -x, y, -z);

            // +Y
            vertexBuf.set(8, -x, y, z);
            vertexBuf.set(9, x, y, z);
            vertexBuf.set(10, x, y, -z);
            vertexBuf.set(11, -x, y, -z);

            // -Y
            vertexBuf.set(12, x, -y, z);
            vertexBuf.set(13, -x, -y, z);
            vertexBuf.set(14, -x, -y, -z);
            vertexBuf.set(15, x, -y, -z);

            // +Z
            vertexBuf.set(16, -x, -y, z);
            vertexBuf.set(17, x, -y, z);
            vertexBuf.set(18, x, y, z);
            vertexBuf.set(19, -x, y, z);

            // -Z
            vertexBuf.set(20, x, -y, -z);
            vertexBuf.set(21, -x, -y, -z);
            vertexBuf.set(22, -x, y, -z);
            vertexBuf.set(23, x, y, -z);
        }


        void Box::initNormalBuf()
        {
            // +X
            normalBuf.set(0, 1.0f, 0.0f, 0.0f);
            normalBuf.set(1, 1.0f, 0.0f, 0.0f);
            normalBuf.set(2, 1.0f, 0.0f, 0.0f);
            normalBuf.set(3, 1.0f, 0.0f, 0.0f);

            // -X
            normalBuf.set(4, -1.0f, 0.0f, 0.0f);
            normalBuf.set(5, -1.0f, 0.0f, 0.0f);
            normalBuf.set(6, -1.0f, 0.0f, 0.0f);
            normalBuf.set(7, -1.0f, 0.0f, 0.0f);

            // +Y
            normalBuf.set(8, 0.0f, 1.0f, 0.0f);
            normalBuf.set(9, 0.0f, 1.0f, 0.0f);
            normalBuf.set(10, 0.0f, 1.0f, 0.0f);
            normalBuf.set(11, 0.0f, 1.0f, 0.0f);

            // -Y
            normalBuf.set(12, 0.0f, -1.0f, 0.0f);
            normalBuf.set(13, 0.0f, -1.0f, 0.0f);
            normalBuf.set(14, 0.0f, -1.0f, 0.0f);
            normalBuf.set(15, 0.0f, -1.0f, 0.0f);

            // +Z
            normalBuf.set(16, 0.0f, 0.0f, 1.0f);
            normalBuf.set(17, 0.0f, 0.0f, 1.0f);
            normalBuf.set(18, 0.0f, 0.0f, 1.0f);
            normalBuf.set(19, 0.0f, 0.0f, 1.0f);

            // -Z
            normalBuf.set(20, 0.0f, 0.0f, -1.0f);
            normalBuf.set(21, 0.0f, 0.0f, -1.0f);
            normalBuf.set(22, 0.0f, 0.0f, -1.0f);
            normalBuf.set(23, 0.0f, 0.0f, -1.0f);
        }

        void Box::initTexcoordBuf()
        {
            // +X
            texcoordBuf.set(0, 1.0f, 0.5f);
            texcoordBuf.set(1, 0.66f, 0.5f);
            texcoordBuf.set(2, 0.66f, 0.25f);
            texcoordBuf.set(3, 1.0f, .25f);

            // -X
            texcoordBuf.set(4, .33f, .5f);
            texcoordBuf.set(5, .0f, .5f);
            texcoordBuf.set(6, .0f, .25f);
            texcoordBuf.set(7, .33f, .25f);

            // +Y
            texcoordBuf.set(8, .33f, .0f);
            texcoordBuf.set(9, .66f, .0f);
            texcoordBuf.set(10, .66f, .25f);
            texcoordBuf.set(11, .33f, .25f);

            // -Y
            texcoordBuf.set(12, .66f, .75f);
            texcoordBuf.set(13, .33f, .75f);
            texcoordBuf.set(14, .33f, .5f);
            texcoordBuf.set(15, .66f, .5f);

            // +Z
            texcoordBuf.set(16, .33f, .75f);
            texcoordBuf.set(17, .66f, .75f);
            texcoordBuf.set(18, .66f, 1.0f);
            texcoordBuf.set(19, .33f, 1.0f);

            // -Z
            texcoordBuf.set(20, .66f, .5f);
            texcoordBuf.set(21, .33f, .5f);
            texcoordBuf.set(22, .33f, .25f);
            texcoordBuf.set(23, .66f, .25f);
        }

        void Box::genDisplayList()
        {
            displayListId = glGenLists(1);
	        if(!displayListId) 
                assert(0);

            glEnableClientState(GL_NORMAL_ARRAY);
	        glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);

            glNormalPointer(GL_FLOAT, 0, normalBuf.get());
	        glVertexPointer(3, GL_FLOAT, 0, vertexBuf.get());
            glTexCoordPointer(2, GL_FLOAT, 0, texcoordBuf.get());

            glNewList(displayListId, GL_COMPILE);
            {
                glDrawArrays(GL_QUADS,0, 24);
            }
            glEndList();

	        glDisableClientState(GL_VERTEX_ARRAY);	// disable vertex arrays
	        glDisableClientState(GL_NORMAL_ARRAY);	// disable normal arrays
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
  	
        }


		bounding::Bounding* Box::_constructBounding()
		{
			if (!_obb)
			{
				_obb = new bounding::OrientedBoundingBox(
					_size.x, _size.y, _size.z);
			}

			return _obb;

		}
		

		void Box::_deconstructBounding(bounding::Bounding*)
		{
			delete _obb;
			_obb = NULL;
		}


        void Box::renderImpl()
        {
			Mesh::renderImpl();
			_materialPtr->begin();
            glCallList(displayListId);  
        }

		MeshPtr BoxCreator::create(const String& name, const Arguments& args)
		{
			try
			{
				return MeshPtr(new Box(name, 
					any_cast<float>(args[0]), // width
					any_cast<float>(args[1]), // height
					any_cast<float>(args[2]) // depth
					));
			}
			catch(const boost::bad_any_cast&)
			{
				throw std::invalid_argument("");
			}
		}
    }
}//namespace DKGL